package mailserver.server;

import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.sql.SQLException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import mailcommon.helpers.PortNumber;
import mailserver.server.exceptions.ServerException;

public class Server {

    public static final int port = PortNumber.port;
    private ServerSocketChannel ssc;
    private Selector selector;
    private HashMap<SocketChannel, ChannelOperator> map;
    

    public Server()
            throws IOException {
        selector = Selector.open();
        map = new HashMap<SocketChannel, ChannelOperator>();
        initChannels();
    }

    private void initChannels()
            throws IOException {
        ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        ssc.socket().bind(new InetSocketAddress(port));
        System.out.println("waiting for clients...");
    }

    public void run() 
            throws CharacterCodingException, SQLException, ServerException, IOException {
        while (true) {
            // check for activities
            int num = selector.select();
            if (num == 0) {
                // have no activities
                // go to next iteration
                continue;
            }
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIter = keys.iterator();

            while (keyIter.hasNext()) {
                SelectionKey key = keyIter.next();

                if (key.isAcceptable()) {
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                    map.put(sc, new ChannelOperator(sc));
                } else if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    try {
                        map.get(sc).run();
                    } catch (Exception ex) {
                        sc.finishConnect();
                        map.remove(sc);
                    }
                }
                keyIter.remove();
            }
            keys.clear();
        }
    }

    public void close()
            throws IOException {
        ssc.close();
    }
}
